/*CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
 C      main.c
 C      Application Name: tcss422-project2
 C      Programmers Names: Evan Phillips, Lucas Cowell, Tony Butterfield
 C      Date created:   May 13, 2011
 C      Date Completed: 
 C      Brief Description: 
 C
 CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "main.h"
#include "src/definitions.h"
#include "src/cpu_sim.h"
#include "src/system_timer.h"
#include "src/io_device.h"
#include "src/process_queue.h"
#include "src/process.h"
#include "src/scheduler.h"




int main(int argc, char * argv[])
{

	int error = 0;
	int num_processes = 4;
	int num_steps = 1000;
	BOOLEAN step = FALSE;

    // we need to get arguments from the command line, don't we...
	switch (argc)
	{
	    case 1:
            break;
	    case 2:
	        if (strcmp(argv[1],"-s") != 0)
	        {
	            printf("\n%s invalid arguments\n - %s [processes] [steps] [-s]\n", argv[0], argv[0]);
	            error = INVALID_ARGUMENT_ERROR;
	        }
	        else
	        {
	            step = TRUE;
	        }
	        break;
	    case 3:
	        if (strcmp(argv[1],"-s") == 0 || strcmp(argv[2],"-s") == 0)
	        {
	            printf("\n%s invalid arguments\n - %s [processes] [steps] [-s]\n", argv[0], argv[0]);
	            error = INVALID_ARGUMENT_ERROR;
	        }
	        else
	        {
	            num_processes = atoi(argv[1]);
	            num_steps = atoi(argv[2]);
	        }
	        break;
	    case 4:
	        if (strcmp(argv[3],"-s") != 0)
	        {
	            printf("\n%s invalid arguments\n - %s [processes] [steps] [-s]\n", argv[0], argv[0]);
                error = INVALID_ARGUMENT_ERROR;
            }
	        else
	        {
	            num_processes = atoi(argv[1]);
	            num_steps = atoi(argv[2]);
	            step = TRUE;
	        }
	        break;
	    default:
            printf("\n%s invalid arguments\n - %s [processes] [steps] [-s]\n", argv[0], argv[0]);
	        error = INVALID_ARGUMENT_ERROR;
	        break;
	}
	if (error != 0) return error;

	printf ("\n%s starting with %d processes.\n\n", argv[0], num_processes);


	// initialize cpu and timer.
    CPU cpu = cpu_init();
    Timer timer = timer_init(TIMER_INTERRUPT_INTERVAL, cpu);

    // create a collection of processes to populate the scheduler's ready queue.
    ProcessQueue processes = generateProceses(num_processes);   // num_processes - have set to 6 for testing.

    // populate ready queue.
    printf("\nInitializing scheduler ready queue:\n");
    int i;
    for (i = 0; i < num_processes; i++)
    {
        error |= sched_setReady(cpu->scheduler, p_queue_dequeue(processes, &error));
        printf("\n");
    }

    printf("Beginning simulation: \n");
    for (i = 0; i < num_steps; i++)
    {
        timer_step(timer);

    }
    printf("\nSimulation terminated after %d steps.\n", num_steps);

    return error;
}

Process generateProducer(int pid, char* name)
{
    Process p = p_init(pid, name, 100, 3);
    p->requests[0]->request_type = IO_DSK_REQUEST;
    p->requests[0]->step_number = 30;
    p->requests[1]->request_type = SYS_SEM_DWN_REQUEST;
    p->requests[1]->step_number = 50;
    p->requests[2]->request_type = SYS_SEM_UP_REQUEST;
    p->requests[2]->step_number = 90;
    return p;
}

Process generateConsumer(int pid, char* name)
{
    Process p = p_init(pid, name, 50, 3);
    p->requests[0]->request_type = SYS_SEM_DWN_REQUEST;
    p->requests[0]->step_number = 10;
    p->requests[1]->request_type = SYS_SEM_UP_REQUEST;
    p->requests[1]->step_number = 15;
    p->requests[2]->request_type = IO_VID_REQUEST;
    p->requests[2]->step_number = 40;
    return p;
}

Process generateUserInterface(int pid, char* name)
{
    Process p = p_init(pid, name, 25, 3);
    p->requests[0]->request_type = IO_KBD_REQUEST;
    p->requests[0]->step_number = 5;
    p->requests[1]->request_type = IO_VID_REQUEST;
    p->requests[1]->step_number = 15;
    p->requests[2]->request_type = IO_DSK_REQUEST;
    p->requests[2]->step_number = 20;
    return p;
}

Process generateBackground(int pid, char* name)
{
    return p_init(pid, name, 20, 0);
}


ProcessQueue generateProceses(int numOfProcesses)
{
    // create an array to hold our processes
    Process* p = (Process*) malloc(sizeof(processStr) * numOfProcesses);

    int i;
    srand(time(NULL));
    for (i = 0; i < numOfProcesses - 1; i++)
    {
    	int r = rand() % 3;
    	switch(r) {
			case 0:
				//this case is a pair, must make sure we wont overflow the array
				if (i < numOfProcesses - 2) {
					p[i] = generateProducer(i+1, "Producer");
					i = i + 1;
					p[i] = generateConsumer(i+1, "Consumer");
				}
				break;
			case 1:
				p[i] = generateUserInterface(i+1, "Terminal");
				break;
			case 2:
				p[i] = generateBackground(i+1, "Background");
				break;
			default:
				break;
    	}
    }

    p[numOfProcesses - 1] = p_init(0, "Idle", 2000,0);

    ProcessQueue pq = p_queue_init();

    // shuffle tasks into a process queue - eventually.  for our testing, just stuff 'em in order.
    for(i = 0; i < numOfProcesses; i++)
    {

       p_queue_enqueue(pq, p[i]);
    }

    return pq;
}


int checkKb(CPU cpu) {
	if (cpu == NULL) return NULL_POINTER_ERROR;
	//put in int because char value range is not defined on all systems (possibly unsigned),
	//EOF is -1 so lets be safe
	int c = getchar();
	if (c != EOF && c != NULL) {
		printf("Keyboard interrupt generated.");
		iodev_throwInterrupt(cpu->kbd);
	}

	return 0;
}



